/**
 * Console Formatter Utils
 * 工具函数集合
 */

(function () {
    'use strict';

    // 导出到全局作用域
    window.ConsoleFormatterUtils = {

        // ========== 配置常量 ==========
        // 内容长度限制常量
        MAX_CONTENT_LENGTH: 2048,

        // 原始类型宽度限制常量
        MAX_VALUE_WIDTH: 80,

        // 每层最大项目数限制常量
        MAX_ITEMS_PER_LEVEL: 100,

        // 默认预览层级深度
        DEFAULT_HEADER_DEPTH: 3,

        // localStorage 键名
        LS_KEYS: {
            DEPTH: 'console_formatter_header_depth',
            LENGTH: 'console_formatter_max_length',
            VALUE_WIDTH: 'console_formatter_value_width',
            AUTO: 'console_formatter_auto_enabled'
        },

        // 样式常量
        STYLES: {
            STRING_COLOR: '#a31515',
            NUMBER_COLOR: '#098658',
            BOOLEAN_COLOR: '#0000ff',
            NULL_COLOR: '#0000ff',
            KEY_COLOR: '#a31515',
            BRACE_COLOR: '#000000',
            COLON_COLOR: '#000000',
            COMMA_COLOR: '#000000',
            HINT_COLOR: '#666',
            FONT_FAMILY: 'monospace',
            FONT_SIZE: '12px',
            FONT_SIZE_SMALL: '11px'
        },

        // ========== 配置管理 ==========
        /**
         * 从 localStorage 加载配置
         */
        loadConfig(key, defaultValue) {
            try {
                const saved = localStorage.getItem(key);
                if (saved !== null) {
                    if (typeof defaultValue === 'number') {
                        const num = Number(saved);
                        return Number.isFinite(num) && num > 0 ? num : defaultValue;
                    }
                    if (typeof defaultValue === 'boolean') {
                        return saved === '1';
                    }
                    return saved;
                }
            } catch (e) {
                // ignore storage errors
            }
            return defaultValue;
        },

        /**
         * 保存配置到 localStorage
         */
        saveConfig(key, value) {
            try {
                localStorage.setItem(key, String(value));
                return true;
            } catch (e) {
                return false;
            }
        },

        /**
         * 初始化默认配置
         */
        initDefaultConfig() {
            // 确保自动格式化默认开启
            if (this.loadConfig(this.LS_KEYS.AUTO, true) === true) {
                this.saveConfig(this.LS_KEYS.AUTO, '1');
            }
        },
        isPrimitive(val) {
            return val === null || (typeof val !== 'object' && typeof val !== 'function');
        },

        /**
         * 截断原始类型值
         */
        truncatePrimitive(value) {
            const s = String(value);
            if (s.length > this.MAX_VALUE_WIDTH) {
                return s.slice(0, this.MAX_VALUE_WIDTH - 1) + '…';
            }
            return s;
        },

        /**
         * 清理值，将无法JSON序列化的值替换为类型描述
         */
        sanitizeValue(value) {
            if (this.isPrimitive(value)) {
                return this.truncatePrimitive(value);
            }
            // 无法序列化，返回类型描述
            if (typeof value === 'function') {
                return '[Function]';
            }
            if (value instanceof Date) {
                return '[Date]';
            }
            if (value instanceof RegExp) {
                return '[RegExp]';
            }
            if (value instanceof Error) {
                return '[Error]';
            }
            if (this.isDomNode(value)) {
                return '[DOM Node]';
            }
            if (this.isWindowLike(value)) {
                return '[Window]';
            }
            // //排查是可以进行序列化否则就
            // try {
            //     JSON.stringify(value);
            // } catch (e) {
            //     console.log('ConsoleFormatter: sanitizeValue non-serializable value detected:', e.message);
            //     return '[unknown]';
            // }
            return value;
        },

        /**
         * 判断是否为普通对象（非数组、非特殊对象）
         */
        isPlainObject(obj) {
            if (!obj || typeof obj !== 'object') return false;
            if (Array.isArray(obj)) return false;
            if (obj.constructor && obj.constructor.name !== 'Object') return false;
            return Object.getPrototypeOf(obj) === Object.prototype;
        },

        /**
         * 判断是否为 Proxy 对象
         */
        isProxy(obj) {
            let data = Object.prototype.toString.call(obj);
            return data === '[object Proxy]' || data === '[object Object]' || data === '[object Array]';
        },

        /**
         * 判断是否为 Window 对象
         */
        isWindowLike(obj) {
            return obj && typeof obj === 'object' && (
                obj === window ||
                obj.window === obj ||
                obj.document ||
                obj.location ||
                obj.navigator
            );
        },

        /**
         * 判断是否为 DOM 节点
         */
        isDomNode(obj) {
            return obj && typeof obj === 'object' && (
                obj.nodeType ||
                obj.nodeName ||
                obj.tagName ||
                (obj.constructor && (
                    obj.constructor.name === 'HTMLElement' ||
                    obj.constructor.name === 'Element' ||
                    obj.constructor.name === 'Node'
                ))
            );
        },

        /**
         * 判断是否为 Error 对象
         */
        isErrorLike(obj) {
            return obj && typeof obj === 'object' && (
                obj instanceof Error ||
                obj.name === 'Error' ||
                obj.message ||
                obj.stack
            );
        },


        /**
         * 根据深度截断对象（处理循环引用）
         */
        truncateByDepth(value, depth, maxItemsPerLevel = this.MAX_ITEMS_PER_LEVEL, visited = new WeakSet()) {
            if (depth <= 0) {
                return '...';
            }

            // 检查循环引用
            if (visited.has(value)) {
                return '[Circular]';
            }

            if (this.isPrimitive(value)) {
                return this.truncatePrimitive(value);
            }

            // 标记当前对象为已访问
            visited.add(value);

            if (Array.isArray(value)) {
                const truncated = [];
                const count = Math.min(value.length, maxItemsPerLevel);

                for (let i = 0; i < count; i++) {
                    // 先清理当前层级的值，再递归处理
                    const sanitizedValue = this.sanitizeValue(value[i]);
                    if (depth > 1 && typeof sanitizedValue === 'object' && sanitizedValue !== null) {
                        truncated.push(this.truncateByDepth(sanitizedValue, depth - 1, maxItemsPerLevel, visited));
                    } else {
                        truncated.push(sanitizedValue);
                    }
                }

                if (value.length > count) {
                    truncated.push('...');
                }

                return truncated;
            }

            if (this.isPlainObject(value)) {
                const truncated = {};
                const keys = Object.keys(value);
                const count = Math.min(keys.length, maxItemsPerLevel);

                for (let i = 0; i < count; i++) {
                    const key = keys[i];
                    // 先清理当前层级的值，再递归处理
                    const sanitizedValue = this.sanitizeValue(value[key]);
                    if (depth > 1 && typeof sanitizedValue === 'object' && sanitizedValue !== null) {
                        truncated[key] = this.truncateByDepth(sanitizedValue, depth - 1, maxItemsPerLevel, visited);
                    } else {
                        truncated[key] = sanitizedValue;
                    }
                }

                if (keys.length > count) {
                    truncated['...'] = '...';
                }

                return truncated;
            }

            return value;
        },

        /**
         * JSON 分词器
         */
        tokenizeJson(jsonString) {
            const tokens = [];
            let i = 0;

            while (i < jsonString.length) {
                const char = jsonString[i];

                if (char === '"') {
                    // String token
                    let value = '"';
                    i++;
                    while (i < jsonString.length && jsonString[i] !== '"') {
                        if (jsonString[i] === '\\' && i + 1 < jsonString.length) {
                            value += jsonString[i] + jsonString[i + 1];
                            i += 2;
                        } else {
                            value += jsonString[i];
                            i++;
                        }
                    }
                    if (i < jsonString.length) {
                        value += '"';
                        i++;
                    }

                    // Check if this is a key (followed by colon)
                    let j = i;
                    while (j < jsonString.length && /\s/.test(jsonString[j])) j++;
                    const isKey = j < jsonString.length && jsonString[j] === ':';

                    tokens.push({
                        type: isKey ? 'key' : 'string',
                        value: value
                    });
                } else if (/\d/.test(char) || char === '-') {
                    // Number token
                    let value = '';
                    while (i < jsonString.length && (/\d/.test(jsonString[i]) || jsonString[i] === '.' || jsonString[i] === 'e' || jsonString[i] === 'E' || jsonString[i] === '+' || jsonString[i] === '-')) {
                        value += jsonString[i];
                        i++;
                    }
                    tokens.push({type: 'number', value: value});
                } else if (char === 't' && jsonString.substr(i, 4) === 'true') {
                    tokens.push({type: 'boolean', value: 'true'});
                    i += 4;
                } else if (char === 'f' && jsonString.substr(i, 5) === 'false') {
                    tokens.push({type: 'boolean', value: 'false'});
                    i += 5;
                } else if (char === 'n' && jsonString.substr(i, 4) === 'null') {
                    tokens.push({type: 'null', value: 'null'});
                    i += 4;
                } else if (char === '{' || char === '}') {
                    tokens.push({type: 'brace', value: char});
                    i++;
                } else if (char === '[' || char === ']') {
                    tokens.push({type: 'bracket', value: char});
                    i++;
                } else if (char === ':') {
                    tokens.push({type: 'colon', value: char});
                    i++;
                } else if (char === ',') {
                    tokens.push({type: 'comma', value: char});
                    i++;
                } else {
                    // Whitespace and other characters
                    tokens.push({type: 'whitespace', value: char});
                    i++;
                }
            }

            return tokens;
        },

        /**
         * 解析 JSON 字符串并添加 HTML 样式
         */
        parseJsonWithStyling(jsonString) {
            try {
                const tokens = this.tokenizeJson(jsonString);
                const styledTokens = tokens.map(token => {
                    switch (token.type) {
                        case 'string':
                            return ['span', {style: `color:${this.STYLES.STRING_COLOR};`}, token.value];
                        case 'number':
                            return ['span', {style: `color:${this.STYLES.NUMBER_COLOR};`}, token.value];
                        case 'boolean':
                            return ['span', {style: `color:${this.STYLES.BOOLEAN_COLOR};`}, token.value];
                        case 'null':
                            return ['span', {style: `color:${this.STYLES.NULL_COLOR};`}, 'null'];
                        case 'key':
                            return ['span', {style: `color:${this.STYLES.KEY_COLOR};`}, token.value];
                        case 'bracket':
                        case 'brace':
                            return ['span', {style: `color:${this.STYLES.BRACE_COLOR};`}, token.value];
                        case 'colon':
                        case 'comma':
                            return ['span', {style: `color:${this.STYLES.BRACE_COLOR};`}, token.value];
                        default:
                            return ['span', {style: `color:${this.STYLES.BRACE_COLOR};`}, token.value];
                    }
                });

                // 返回一个包装的 div 元素，包含所有样式化的 token
                return ['div', {
                    style: `font-family:${this.STYLES.FONT_FAMILY};font-size:${this.STYLES.FONT_SIZE};line-height:1.2;white-space:pre;`
                }, ...styledTokens];
            } catch (e) {
                return ['span', {style: 'color:#000000;'}, jsonString];
            }
        },

        /**
         * 渲染 JSON 预览（带语法高亮和长度限制）
         */
        renderJsonPreview(value, depth, maxItemsPerLevel = 100) {
            try {
                const truncated = this.truncateByDepth(value, depth, maxItemsPerLevel);
                
                // 最终清理循环引用，确保可以JSON序列化，传递深度参数避免无限递归
                const finalCleaned = this.finalCircularCleanup(truncated, depth);
                let jsonString = JSON.stringify(finalCleaned, null, 2);

                // 检查长度限制
                if (jsonString.length > this.MAX_CONTENT_LENGTH) {
                    const truncatedJson = jsonString.substring(0, this.MAX_CONTENT_LENGTH);
                    const hint = `... [截断 ${jsonString.length}/${this.MAX_CONTENT_LENGTH}]`;

                    // 使用带语法高亮的 JSONML 渲染，包含简洁的截断提示
                    const truncatedStyled = this.parseJsonWithStyling(truncatedJson);
                    const hintStyled = ['span', {
                        style: `color:${this.STYLES.HINT_COLOR};font-size:${this.STYLES.FONT_SIZE_SMALL};margin-left:4px;`
                    }, hint];

                    return ['div', {
                        style: `font-family:${this.STYLES.FONT_FAMILY};font-size:${this.STYLES.FONT_SIZE};line-height:1.2;white-space:pre;`
                    }, truncatedStyled, hintStyled];
                }

                // 使用带语法高亮的 JSONML 渲染
                return this.parseJsonWithStyling(jsonString);
            } catch (e) {
                console.log('ConsoleFormatter: renderJsonPreview error:', e.message);
                return null
            }
        },

        /**
         * 最终清理循环引用，确保对象可以JSON序列化
         * @param {*} obj - 要清理的对象
         * @param {number} depth - 当前深度
         * @param {WeakSet} visited - 已访问的对象集合
         */
        finalCircularCleanup(obj, depth = 0, visited = new WeakSet()) {
            // 深度限制，避免无限递归
            if (depth <= 0) {
                return '...';
            }
            
            // 检查循环引用
            if (visited.has(obj)) {
                return '[Circular]';
            }
            
            // 处理原始类型
            if (this.isPrimitive(obj)) {
                return obj;
            }
            
            // 使用语言处理器处理特殊对象
            if (typeof window.LanguageHandlers !== 'undefined') {
                const processed = window.LanguageHandlers.processSpecialObject(obj, depth, visited, this);
                if (processed !== obj) {
                    return processed;
                }
            }
            
            // 过滤其他特殊对象
            if (this.isWindowLike(obj)) {
                return '[Window]';
            }
            
            if (this.isDomNode(obj)) {
                return '[DOM Node]';
            }
            
            if (this.isErrorLike(obj)) {
                return '[Error]';
            }
            
            // 标记当前对象为已访问
            visited.add(obj);
            
            // 处理数组
            if (Array.isArray(obj)) {
                return obj.map(item => this.finalCircularCleanup(item, depth - 1, visited));
            }
            
            // 处理对象
            if (typeof obj === 'object' && obj !== null) {
                const cleaned = {};
                try {
                    // 使用 Object.keys 获取可枚举属性，避免 for...in 循环的问题
                    const keys = Object.keys(obj);
                    for (const key of keys) {
                        try {
                            // 安全地访问属性值
                            const value = obj[key];
                            cleaned[key] = this.finalCircularCleanup(value, depth - 1, visited);
                        } catch (e) {
                            // 如果无法访问属性，跳过该属性
                            cleaned[key] = '[Inaccessible]';
                        }
                    }
                } catch (e) {
                    // 如果无法获取对象的键，返回通用描述
                    return '[Object - Inaccessible]';
                }
                return cleaned;
            }
            
            return obj;
        }
    };

})();
